package org.dromara.northstar.gateway.okx;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.okex.open.api.bean.other.WSOrderTradeChannel;
import com.okex.open.api.bean.trade.param.CancelOrder;
import com.okex.open.api.bean.trade.param.PlaceOrder;
import com.okex.open.api.config.APIConfiguration;
import com.okex.open.api.service.account.AccountAPIService;
import com.okex.open.api.service.account.impl.AccountAPIServiceImpl;
import com.okex.open.api.service.trade.TradeAPIService;
import com.okex.open.api.service.trade.impl.TradeAPIServiceImpl;
import com.okex.open.api.utils.JsonUtils;
import com.okex.open.api.ws.TradePrivateWebSocketClient;
import org.dromara.northstar.common.constant.ChannelType;
import org.dromara.northstar.common.constant.ConnectionState;
import org.dromara.northstar.common.event.FastEventEngine;
import org.dromara.northstar.common.event.NorthstarEventType;
import org.dromara.northstar.common.model.GatewayDescription;
import org.dromara.northstar.common.model.core.*;
import org.dromara.northstar.gateway.IContract;
import org.dromara.northstar.gateway.IMarketCenter;
import org.dromara.northstar.gateway.TradeGateway;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import xyz.redtorch.pb.CoreEnum;

import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * OKX交易网关适配器
 *
 * @author zt
 */
@Slf4j
public class OkxTradeGatewayAdapter implements TradeGateway {

    public static final String SERVICE_URL = "https://www.okx.com/";

    OkxGatewaySettings settings;

    protected FastEventEngine feEngine;

    private static final Logger logger = LoggerFactory.getLogger(OkxTradeGatewayAdapter.class);

    @Getter
    private boolean connected;

    private final GatewayDescription gd;

    private ConnectionState connState = ConnectionState.DISCONNECTED;

    private final IMarketCenter mktCenter;

    private Timer accountInfoTimer;

    private AccountAPIService accountAPIService;
    private TradeAPIService tradeAPIService;

    private final TradePrivateWebSocketClient tradePrivateWebSocketClient;

    private final ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    private static final int RECONNECT_DELAY = 5; // 重连延迟，单位：秒


    //    持仓数据
    private final Map<String, JSONObject> positionMap = new HashMap<>();

    protected Map<String, SubmitOrderReq> submitOrderReqFieldMap = new HashMap<>();

    //    挂单数据
    protected Map<String, Order> orderMap = new HashMap<>();

    public OkxTradeGatewayAdapter(FastEventEngine feEngine, GatewayDescription gd, IMarketCenter mktCenter) {
        this.settings = (OkxGatewaySettings) gd.getSettings();
        this.feEngine = feEngine;
        this.gd = gd;
        this.mktCenter = mktCenter;
        APIConfiguration config = getApiConfiguration(settings);
        this.accountAPIService = new AccountAPIServiceImpl(config);
        this.tradeAPIService = new TradeAPIServiceImpl(config);
        this.tradePrivateWebSocketClient = new TradePrivateWebSocketClient(gd.getGatewayId(), settings.getApiKey(), settings.getSecretKey(), settings.getPassphrase(), settings.getAccountType());
    }

    private static final int MAX_RETRIES = 10; // 最大重试次数
    private int retryCount = 0; // 当前重试次数

    /**
     * 尝试连接 WebSocket。如果连接失败，会自动重试最多 MAX_RETRIES 次。
     */
    @Override
    public void connect() {
        if (attemptConnection()) {
            retryCount = 0; // 成功连接后重置重试计数
        } else {
            log.error("连续重试 {} 次后仍未成功，不再重试", MAX_RETRIES);
        }
    }

    /**
     * 循环尝试连接 WebSocket，直到成功连接或达到最大重试次数。
     *
     * @return
     */
    private boolean attemptConnection() {
        while (retryCount < MAX_RETRIES) {
            try {
                initializeConnection();
                return true; // 连接成功，返回 true
            } catch (Exception e) {
                retryCount++;
                log.error("账户网关重新连接 (重试次数: " + retryCount + ")", e);
                disconnect();
                if (retryCount < MAX_RETRIES) {
                    sleepBeforeRetry();
                }
            }
        }
        return false; // 达到最大重试次数，返回 false
    }

    /**
     * 初始化 WebSocket 连接，包括网关连接、订阅订单频道和执行连接后的工作。
     *
     * @throws Exception
     */
    private void initializeConnection() throws Exception {
        // 执行连接后的工作
        connectWork();
        // 网关连接
        connectSocket();
    }

    private void connectSocket() {
        tradePrivateWebSocketClient.connection(message -> {
            try {
                if (message.equals("onFailure")) {
                    this.connectSocket();
                }
                log.info("okx交易网关信息推送:[{}]", message);
                if (!isValidJson(message)) {
                    return;
                }
                if (message.contains("event")) {
                    onEvent(message);
                } else if (message.contains("arg") && message.contains("data")) {
                    onOrders(message);
                }
            } catch (Exception e) {
                log.error("okx交易网关信息推送解析异常:[{}]", message, e);
            }
        });
    }


    private void onOrders(String message) {
        WSOrderTradeChannel wsOrderTradeChannel = JSON.parseObject(message, WSOrderTradeChannel.class);
        switch (wsOrderTradeChannel.getArg().getChannel()) {
            case "orders":
                orderTradeUpdate(wsOrderTradeChannel);
                break;
            default:
                log.error("okx交易网关信息推送:[{}]", message);
                break;
        }
    }

    private void onEvent(String message) {
        JSONObject event = JSON.parseObject(message);
        switch (event.getString("event")) {
            case "login":
                if (event.getString("code").equals("0")) {
                    log.info("okx交易网关登录成功");
                    subscribeOrders();
                }
                break;
            case "subscribe":
                log.info("okx交易网关订阅结果 {}", event);
                break;
            default:
                break;
        }
    }


    /**
     * 订阅 orders
     */
    private void subscribeOrders() {
        if (tradePrivateWebSocketClient.getWebSocket() == null) {
            return;
        }
        ArrayList<Map<String, Object>> channelList = new ArrayList<>();
        Map<String, Object> spotMap = new HashMap<>();
        spotMap.put("channel", "orders");
        spotMap.put("instType", "SPOT");
        channelList.add(spotMap);
        Map<String, Object> swapMap = new HashMap<>();
        swapMap.put("channel", "orders");
        swapMap.put("instType", "SWAP");
        channelList.add(swapMap);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("op", "subscribe");
        jsonObject.put("args", channelList);
        log.info("订阅okx 订单频道");
        tradePrivateWebSocketClient.sendMessage(jsonObject.toJSONString());
    }

    private void orderTradeUpdate(WSOrderTradeChannel orderTrade) {
        List<WSOrderTradeChannel.DataDTO> data = orderTrade.getData();
        if (data.isEmpty()) {
            return;
        }
        for (WSOrderTradeChannel.DataDTO dto : data) {
            if (submitOrderReqFieldMap.containsKey(dto.getClOrdId())) {
                SubmitOrderReq orderReq = submitOrderReqFieldMap.get(dto.getClOrdId());
                Contract contract = orderReq.contract();
                Instant e = Instant.ofEpochMilli(dto.getUTime());

                LocalTime tradeTime = e.atZone(ZoneId.systemDefault()).toLocalTime();
                LocalDate tradeDate = e.atZone(ZoneId.systemDefault()).toLocalDate();
                LocalDate tradingDay = LocalDate.now();

                //订单末次成交量按照最小交易精度转换
                int executedQty = Math.abs(Double.valueOf(dto.getFillSz() / contract.multiplier()).intValue());
                //订单原始数量
                int origQty = Math.abs(Double.valueOf(dto.getSz() / contract.multiplier()).intValue());
                CoreEnum.DirectionEnum dBuy = null;
                CoreEnum.OffsetFlagEnum offsetFlag = null;
                if ("buy".equals(dto.getSide())) {
                    dBuy = CoreEnum.DirectionEnum.D_Buy;
                    offsetFlag = CoreEnum.OffsetFlagEnum.OF_Open;
                } else if ("sell".equals(dto.getSide())) {
                    dBuy = CoreEnum.DirectionEnum.D_Sell;
                    offsetFlag = CoreEnum.OffsetFlagEnum.OF_Close;
                }
                Order.OrderBuilder buildered = Order.builder();
                buildered.orderId(dto.getOrdId());
                buildered.originOrderId(orderReq.originOrderId());
                buildered.gatewayId(gd.getGatewayId());
                buildered.contract(contract);
                buildered.direction(dBuy);
                buildered.offsetFlag(offsetFlag);
                buildered.price(dto.getLastPx());
                buildered.totalVolume(origQty);
                buildered.tradedVolume(executedQty);
                buildered.tradingDay(tradingDay);
                buildered.updateTime(LocalTime.now());
                buildered.orderDate(LocalDate.now());
                /**
                 * 订单状态
                 * canceled：撤单成功
                 * live：等待成交
                 * partially_filled：部分成交
                 * filled：完全成交
                 * mmp_canceled：做市商保护机制导致的自动撤单
                 */
                if (dto.getState().equals("filled")) {
                    //清除挂单信息
                    orderMap.remove(dto.getClOrdId());
                    buildered.statusMsg("全部成交").orderStatus(CoreEnum.OrderStatusEnum.OS_AllTraded);
                } else if (dto.getState().equals("canceled")) {
                    buildered.statusMsg("已撤单").orderStatus(CoreEnum.OrderStatusEnum.OS_Canceled);
                } else if (dto.getState().equals("partially_filled")) {
                    buildered.statusMsg("部分成交还在队列中").orderStatus(CoreEnum.OrderStatusEnum.OS_PartTradedQueueing);
                } else if (dto.getState().equals("live")) {
                    //储存挂单信息
                    orderMap.put(dto.getClOrdId(), buildered.build());
                    buildered.statusMsg("已挂单").orderStatus(CoreEnum.OrderStatusEnum.OS_NoTradeQueueing);
                }
                Order build = buildered.build();
                feEngine.emitEvent(NorthstarEventType.ORDER, build);
                if (dto.getState().equals("filled")) {
                    Trade trade = Trade.builder()
                            .tradeDate(tradeDate)
                            .tradeTime(tradeTime)
                            .tradingDay(tradingDay)
                            .tradeTimestamp(dto.getUTime())
                            .direction(orderReq.direction())
                            .offsetFlag(orderReq.offsetFlag())
                            .contract(orderReq.contract())
                            .orderId(orderReq.originOrderId())
                            .originOrderId(orderReq.originOrderId())
                            .price(dto.getAvgPx())
                            .volume(orderReq.volume())
                            .gatewayId(orderReq.gatewayId())
                            .tradeType(CoreEnum.TradeTypeEnum.TT_Common)
                            .priceSource(CoreEnum.PriceSourceEnum.PSRC_LastPrice)
                            .build();
                    feEngine.emitEvent(NorthstarEventType.TRADE, trade);
                }
                log.info("okx交易网关 [{}] 订单反馈：{} {} {} {} {}", build.gatewayId(), build.orderDate(), build.updateTime(), build.originOrderId(), build.orderStatus(), build.statusMsg());
            }
        }
    }

    private void sleepBeforeRetry() {
        // 可选：等待一段时间再重试，避免频繁重试导致的问题
        try {
            Thread.sleep(1000); // 等待1秒
        } catch (InterruptedException ie) {
            Thread.currentThread().interrupt();
        }
    }


    @Override
    public void disconnect() {
        if (tradePrivateWebSocketClient.getWebSocket() != null) {
            tradePrivateWebSocketClient.getWebSocket().close(1000, "Long time no message was sent or received！");
        }
        tradePrivateWebSocketClient.setWebSocket(null);
        log.info("[{}] 账户网关断开", gd.getGatewayId());
        connected = false;
        connState = ConnectionState.DISCONNECTED;
        accountInfoTimer.cancel();
        feEngine.emitEvent(NorthstarEventType.LOGGED_OUT, gd.getGatewayId());
    }

    @Override
    public boolean getAuthErrorFlag() {
        return false;
    }

    @Override
    public ConnectionState getConnectionState() {
        return connState;
    }

    @Override
    public String submitOrder(SubmitOrderReq submitOrderReq) {
        if (!isConnected()) {
            throw new IllegalStateException("网关未连线");
        }
        log.info("[{}] 网关收到下单请求,参数:[{}]", gd.getGatewayId(), submitOrderReq);
        Contract contract = submitOrderReq.contract();
        CoreEnum.DirectionEnum direction = submitOrderReq.direction();
        CoreEnum.OffsetFlagEnum offsetFlag = submitOrderReq.offsetFlag();
        //数量 * 最小交易数量
        Double quantity = submitOrderReq.volume() * contract.multiplier();
        BigDecimal db = new BigDecimal(quantity.toString());
        String formattedQuantity = db.toPlainString();

        String side;
        String posSide;
        String ordType;
        // 开仓
        //开多：买多BUY、LONG
        //开空：卖空SELL、SHORT
        if (CoreEnum.OffsetFlagEnum.OF_Open.getNumber() == offsetFlag.getNumber()) {
            side = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "buy" : "sell";
            ordType = (submitOrderReq.price() == 0) ? "market" : "limit";
            // 持仓方向
            posSide = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "long" : "short";
        } else {
            // 平仓
            //平空：卖空BUY、SHORT
            //平多：卖多SELL、LONG
            side = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "buy" : "sell";
            ordType = (submitOrderReq.price() == 0) ? "market" : "limit";
            // 平仓方向
            posSide = (CoreEnum.DirectionEnum.D_Buy.getNumber() == direction.getNumber()) ? "short" : "long";
        }

        String clOrdId = submitOrderReq.originOrderId().replaceAll("-", "");
        PlaceOrder order = PlaceOrder.builder()
                .instId(contract.symbol())
                .clOrdId(clOrdId)
                .side(side)
                .ordType(ordType)
                .sz(formattedQuantity)
                .px(String.valueOf(submitOrderReq.price()))
                .build();
//        	交易模式
        if (CoreEnum.ProductClassEnum.SWAP.equals(contract.productClass())) {
//            保证金模式：isolated：逐仓 ；cross：全仓  默认全仓
            order.setTdMode("cross");
            order.setPosSide(posSide);
        } else {
//             非保证金模式：cash：
            order.setTdMode("cash");
        }
        JSONObject jsonObject = tradeAPIService.placeOrder(order);
        submitOrderReqFieldMap.put(clOrdId, submitOrderReq);
        log.info("[{}] 网关收到下单返回,响应:[{}]", gd.getGatewayId(), jsonObject);
        //查询全部挂单
        currentAllOpenOrders();
        return submitOrderReq.originOrderId();
    }

    @Override
    public boolean cancelOrder(String originOrderId) {
        if (!isConnected()) {
            throw new IllegalStateException("网关未连线");
        }
        String clOrdId = originOrderId.replaceAll("-", "");
        if (!orderMap.containsKey(clOrdId)) {
            return true;
        }
        log.info("[{}] 网关收到撤单请求", gd.getGatewayId());
        Order order = orderMap.get(clOrdId);
        String symbol = order.contract().symbol();
        JSONObject jsonObject = tradeAPIService.cancelOrder(CancelOrder.builder().clOrdId(clOrdId).instId(symbol).build());
        JSONArray data = jsonObject.getJSONArray("data");
        if (data.isEmpty()) {
            return false;
        }
        JSONObject cancel = IntStream.range(0, data.size())
                .mapToObj(data::getJSONObject)
                .filter(item -> Objects.equals(item.getString("clOrdId"), clOrdId)).findFirst()
                .orElse(null);
        if (cancel == null) {
            return false;
        }
        return Objects.equals(cancel.getString("sCode"), "0") || Objects.equals(cancel.getString("sCode"), "51400");
    }

    @Override
    public GatewayDescription gatewayDescription() {
        gd.setConnectionState(getConnectionState());
        return gd;
    }

    @Override
    public String gatewayId() {
        return gd.getGatewayId();
    }


    private void connectWork() {
        log.debug("[{}] 账户网关连线", gd.getGatewayId());
        connected = true;
        connState = ConnectionState.CONNECTED;
        feEngine.emitEvent(NorthstarEventType.LOGGED_IN, gd.getGatewayId());
        CompletableFuture.runAsync(() -> feEngine.emitEvent(NorthstarEventType.GATEWAY_READY, gd.getGatewayId()),
                CompletableFuture.delayedExecutor(2, TimeUnit.SECONDS));
        currentAllOpenOrders();
        accountInfoTimer = new Timer("OkxAccountInfoTimer", true);
//       每隔 1000 毫秒（即每秒）执行一次任务。初始延迟为 0，表示立即开始第一次执行。
        accountInfoTimer.scheduleAtFixedRate(new TimerTask() {
            @Override
            public void run() {
                try {
                    JSONObject positionsJson = accountAPIService.getPositions(null, null, null);
                    JSONArray positions = positionsJson.getJSONArray("data");
                    List<JSONObject> positionList = positions.stream().map(item -> BeanUtil.toBean(item, JSONObject.class)).filter(item -> item.getDoubleValue("pos") != 0).collect(Collectors.toList());
                    List<String> ccyList = positionList.stream().map(item -> item.getString("instId").split("-")[0]).toList();
                    //账户事件
                    JSONObject accountInfo = accountAPIService.getBalance(CoreEnum.CurrencyEnum.USDT.name());
                    getAccountField(accountInfo);
                    List<List<String>> split = CollUtil.split(ccyList, 20);
                    for (List<String> list : split) {
                        String ccy = String.join(",", list);
                        //查询现货资产
                        JSONObject balance = accountAPIService.getBalance(ccy);
                        JSONObject data = balance.getJSONArray("data").getJSONObject(0);
                        JSONArray details = data.getJSONArray("details");
                        List<JSONObject> collect = details.stream().map(item -> BeanUtil.toBean(item, JSONObject.class)).toList();
                        for (JSONObject jsonObject : collect) {
                            jsonObject.put("instId", jsonObject.getString("ccy") + "-" + CoreEnum.CurrencyEnum.USDT.name());
                            jsonObject.put("posSide", "long");
                            jsonObject.put("pos", jsonObject.getDoubleValue("eq"));
                            jsonObject.put("uTime", jsonObject.getLong("uTime"));
                        }
                        positionList.addAll(collect);
                    }


                    //当前持仓map
                    Map<String, JSONObject> newPositionMap = positionList.stream().collect(Collectors.toMap(dto -> dto.getString("instId") + "@" + dto.getString("posSide"), Function.identity(), (a, b) -> a, HashMap::new));
                    for (JSONObject position : positionList) {
                        String symbol = position.getString("instId");
                        String positionSide = position.getString("posSide");
                        String key = symbol + "@" + positionSide;
                        if (!positionMap.containsKey(key)) {
                            positionMap.put(key, position);
                        }
                    }
                    //查出平仓的合约发送持仓事件-持仓数量为0
                    Iterator<String> iterator = positionMap.keySet().iterator();
                    while (iterator.hasNext()) {
                        String key = iterator.next();
                        // positionMap中存在但是newPositionMap没有的position
                        if (!newPositionMap.containsKey(key)) {
                            JSONObject closeAPosition = positionMap.get(key);
                            closeAPosition.put("pos", 0);
                            positionList.add(closeAPosition);
                        }
                    }
                    for (JSONObject position : positionList) {
                        String instId = position.getString("instId");
                        String posSide = position.getString("posSide");
                        CoreEnum.PositionDirectionEnum posDir = "long".equals(posSide) ? CoreEnum.PositionDirectionEnum.PD_Long : CoreEnum.PositionDirectionEnum.PD_Short;
                        IContract contract = mktCenter.getContract(ChannelType.OKX, instId);
                        Contract contracted = contract.contract();
                        //持仓数量按照最小交易精度转换
                        int positionAmt = Math.abs(Double.valueOf(position.getDoubleValue("pos") / contracted.multiplier()).intValue());
                        Double useMargin = position.getDoubleValue("imr");
//                        已实现收益
                        Double realizedPnl = position.getDoubleValue("realizedPnl");
//                        最新标记价格 - 开仓价格
                        Double openPriceDiff = position.getDoubleValue("markPx") - position.getDoubleValue("avgPx");
                        Position pos = Position.builder()
                                .positionId(contracted.unifiedSymbol() + "@" + posDir)
                                .gatewayId(gd.getGatewayId())
                                .positionDirection(posDir)
                                .position(positionAmt)
                                .tdPosition(positionAmt)
                                .ydPosition(positionAmt)
                                .contract(contracted)
//                                .frozen(frozen)
//                                .tdFrozen(tdFrozen)
//                                .ydFrozen(ydFrozen)
                                .openPrice(position.getDoubleValue("avgPx"))
                                .openPriceDiff(openPriceDiff)
                                .positionProfit(position.getDoubleValue("upl"))
                                .positionProfitRatio(position.getDoubleValue("uplRatio"))
                                .contractValue(realizedPnl)
                                .useMargin(useMargin)
                                .exchangeMargin(useMargin)
                                .updateTimestamp(position.getLong("uTime"))
                                .build();
                        logger.trace("合成持仓对象：{}", JSON.toJSONString(pos));
                        feEngine.emitEvent(NorthstarEventType.POSITION, pos);
                    }
                } catch (Exception e) {
                    log.error("账户事件-持仓时事件异常", e);
                }
            }
        }, 0, 1000);
    }


    /**
     * 获取未成交订单列表
     * 返回结果的数量，最大为100，默认100条
     */
    private void currentAllOpenOrders() {
        fetchAndProcessOrders(null);
    }

    private void fetchAndProcessOrders(String lastOrderId) {
        // 获取订单列表
        JSONObject orderList = tradeAPIService.getOrderList(null, null, null, null, null, null, null, lastOrderId, "100");
        JSONArray data = orderList.getJSONArray("data");
        if (data.isEmpty()) {
            return;
        }
        List<JSONObject> openOrderList = data.stream()
                .map(item -> BeanUtil.toBean(item, JSONObject.class))
                .collect(Collectors.toList());
        // 执行订单处理逻辑
        executeOrder(openOrderList);
        // 检查是否需要继续获取下一页
        if (openOrderList.size() >= 100) {
            // 获取最后一个订单的ID
            String lastId = openOrderList.get(openOrderList.size() - 1).getString("ordId");
            // 递归获取下一页订单
            fetchAndProcessOrders(lastId);
        }
    }

    /**
     * 装配挂单
     *
     * @param openOrderList
     */
    private void executeOrder(List<JSONObject> openOrderList) {
        //维护订单ID和symbol的map
        for (JSONObject order : openOrderList) {
            IContract contract = mktCenter.getContract(ChannelType.OKX, order.getString("instId"));
            Contract contracted = contract.contract();
            String side = order.getString("side");
            CoreEnum.DirectionEnum dBuy = null;
            CoreEnum.OffsetFlagEnum offsetFlag = null;
            if ("buy".equals(side)) {
                dBuy = CoreEnum.DirectionEnum.D_Buy;
                offsetFlag = CoreEnum.OffsetFlagEnum.OF_Open;
            } else if ("sell".equals(side)) {
                dBuy = CoreEnum.DirectionEnum.D_Sell;
                offsetFlag = CoreEnum.OffsetFlagEnum.OF_Close;
            }
            Order orderBuilder = getOrderBuilder(dBuy, order, contracted, offsetFlag);
            orderMap.put(order.getString("clOrdId"), orderBuilder);
        }
    }

    private Order getOrderBuilder(CoreEnum.DirectionEnum directionEnum, JSONObject orderJson, Contract contract, CoreEnum.OffsetFlagEnum offsetFlag) {
        Order.OrderBuilder orderBuilder = Order.builder();
        //数量 * 最小交易数量
        int origQty = (int) Math.round(orderJson.getDoubleValue("sz") * Math.pow(10, contract.quantityPrecision()));
        int executedQty = (int) Math.round(orderJson.getDoubleValue("fillSz") * Math.pow(10, contract.quantityPrecision()));
        orderBuilder.orderId(orderJson.getString("ordId"));
        orderBuilder.originOrderId(orderJson.getString("clOrdId"));
        orderBuilder.direction(directionEnum);
        orderBuilder.offsetFlag(offsetFlag);
        //orderBuilder.setOrderStatus(CoreEnum.OrderStatusEnum.OS_AllTraded);
        orderBuilder.price(orderJson.getDoubleValue("px"));
        orderBuilder.totalVolume(origQty);
        orderBuilder.tradedVolume(executedQty);
        orderBuilder.contract(contract);
        orderBuilder.gatewayId(gd.getGatewayId());
        orderBuilder.statusMsg("已挂单");
        orderBuilder.orderStatus(CoreEnum.OrderStatusEnum.OS_NoTradeQueueing);
        orderBuilder.updateTime(getLocalTime(orderJson.getLong("uTime")));
        Order order = orderBuilder.build();
        feEngine.emitEvent(NorthstarEventType.ORDER, order);
        return order;
    }

    /**
     * 转为 时间
     *
     * @param uTime
     * @return
     */
    private static LocalTime getLocalTime(Long uTime) {
        // 将时间戳转换为 Instant 对象
        Instant instant = Instant.ofEpochMilli(uTime);
        // 将 Instant 对象转换为 LocalTime 对象，使用系统默认时区
        LocalTime updateTime = instant.atZone(ZoneId.systemDefault()).toLocalTime();
        return updateTime;
    }


    /**
     * 依据配置编写API配置
     *
     * @return
     */
    private APIConfiguration getApiConfiguration(OkxGatewaySettings settings) {
        APIConfiguration config = new APIConfiguration();
        config.setDomain(SERVICE_URL);
        //secretKey,api注册成功后页面上有
        config.setApiKey(settings.getApiKey());
        config.setSecretKey(settings.getSecretKey());
        //Passphrase忘记后无法找回
        config.setPassphrase(settings.getPassphrase());
//        是否模拟盘的请求
        config.setxSimulatedTrading(settings.getAccountType().equals("1") ? "1" : "0");
//        是否打印请求
        config.setPrint(false);
        return config;
    }

    private void getAccountField(JSONObject jsonObject) {
        JSONObject data = jsonObject.getJSONArray("data").getJSONObject(0);
        JSONObject details = data.getJSONArray("details").getJSONObject(0);
        Account accountBuilder = Account.builder()
                .accountId(gd.getGatewayId())
                .available(details.getDoubleValue("availBal"))
                .balance(details.getDoubleValue("cashBal"))
                .closeProfit(details.getDoubleValue("upl"))
                //TODO，ETH/BTC期货合约将按照BUSD手续费表计。这里币安返回的feeTier是手续费等级,0=0.0200%/0.0500%(USDT-Maker / Taker),暂时写死后续处理
                .commission(Double.valueOf(0.0002))
                .gatewayId(gd.getGatewayId())
                .currency(CoreEnum.CurrencyEnum.USDT)
                .margin(details.getDoubleValue("imr"))
                .positionProfit(details.getDoubleValue("upl"))
                .build();
        feEngine.emitEvent(NorthstarEventType.ACCOUNT, accountBuilder);
    }

    /**
     * 判断给定的字符串是否是有效的 JSON。
     *
     * @param jsonString 要检查的字符串
     * @return 如果是有效的 JSON，返回 true；否则返回 false。
     */
    public static boolean isValidJson(String jsonString) {
        try {
            final ObjectMapper mapper = new ObjectMapper();
            mapper.readTree(jsonString);
            return true;
        } catch (Exception e) {
            return false;
        }
    }


}
